/*
	by Ilija
*/

#include "GLClient.h"
#include "../core/mymath.h"
#include "../core/foreach.h"

GLClient::GLClient(GLEngine* engine)
{
	m_Engine = engine;
	m_Desktop = NULL;
	m_PopupMenu = NULL;
	m_HoveredCtl = NULL;
	m_ClickedCtl = NULL;
	m_ActiveCtl = NULL;
}


GLClient::~GLClient()
{
}


void GLClient::update(float dt)
{
    GLEvent e;
    //tick tasks
    foreach (GLTask&, t, m_TaskList)
    {
        t.timeout -= dt;
        if (t.timeout <= 0.0f)
        {
            e.type = EVT_Task;
            e.task.id = t.id;
            e.task.data = t.data;
            onEvent(e);
            remove_item(t, m_TaskList);
        }
    }
    //update event
    onUpdate(dt);
}

void GLClient::draw()
{
    onDraw();
    //gui
    if (m_Desktop)
    {
        glBeginSceneGUI(m_Desktop->getWidth(), m_Desktop->getHeight());
        m_Desktop->drawControls(m_HoveredCtl, m_ClickedCtl, m_ActiveCtl);
        glEndSceneGUI();
    }
}

void GLClient::refresh()
{
    mouse.flush();
    keyboard.flush();
    m_HoveredCtl = NULL;
    m_ClickedCtl = NULL;
    m_ActiveCtl = NULL;
}

void GLClient::receiveEvent(GLEvent e)
{
    //reise event
    onEvent(e);
    //gui events
    if (m_Desktop)
    {
        int x, y;
        GLGuiControl* ctl;
        switch (e.type)
        {
        case EVT_KeyPress:
            if (m_ActiveCtl && m_ActiveCtl->getType() == GUI_Textbox)
            {
                if (e.key.code == KEY_BACKSPACE)
                {
                    String s = m_ActiveCtl->getText();
                    s.erase(1);
                    m_ActiveCtl->setText(s);
                }
            }
            break;
        case EVT_KeyChar:
            if (m_ActiveCtl && m_ActiveCtl->getType() == GUI_Textbox)
            {
                String s = m_ActiveCtl->getText();
                s += e.key.name;
                m_ActiveCtl->setText(s);
            }
            break;
        case EVT_MouseMove:
            x = (float)e.mouse.x / getEngine()->getWidth() * m_Desktop->getWidth();
            y = (float)e.mouse.y / getEngine()->getHeight() * m_Desktop->getHeight();
            ctl = m_Desktop->pickTopControl(x, y);
            m_HoveredCtl = ctl;
            if (m_HoveredCtl == m_Desktop)
                m_HoveredCtl = NULL;
            //item hover
            if (ctl && ctl->getItemsCount())
            {
                ctl->setHoverItem(ctl->hitItem(x, y));
            }
            //dragged controls
            if (m_ClickedCtl)
            {
                ctl = m_ClickedCtl;
                if (ctl->getType() == GUI_HSlider)
                {
                    rectf b = ctl->getBounds();
                    int val = (float)(x - b.AA.x) / ctl->getWidth() * ctl->getMaxValue() + 0.5;
                    ctl->setValue(val);
                }
                else if (ctl->getType() == GUI_VSlider)
                {
                    rectf b = ctl->getBounds();
                    int val = (float)(y - b.AA.y) / ctl->getHeight() * ctl->getMaxValue() + 0.5;
                    ctl->setValue(val);
                    if (ctl->getParent() && ctl->getParent()->getType() == GUI_Listbox)
                    {
                        ctl->getParent()->setTopItem(ctl->getValue(), false);
                    }
                }
            }
            break;
        case EVT_MousePress:
            if (m_HoveredCtl)
            {
                x = (float)mouse.x / getEngine()->getWidth() * m_Desktop->getWidth();
                y = (float)mouse.y / getEngine()->getHeight() * m_Desktop->getHeight();
                ctl = m_HoveredCtl;
                m_ClickedCtl = m_HoveredCtl;
                m_ActiveCtl = m_HoveredCtl;
                //item select
                if (ctl->getItemsCount())
                {
                    int ii = ctl->hitItem(x, y);
                    if (ii >= 0 && ii < ctl->getItemsCount())
                    {
                        ctl->setCurrentItem(ii);
                        //gui item event
                        GLEvent evt;
                        evt.type = EVT_GuiClick;
                        evt.gui.ctl = ctl;
                        evt.gui.id = ctl->getId();
                        evt.gui.param = ii;
                        onEvent(evt);
                    }
                }
                //hide popupmenu
                if (m_PopupMenu)
                    m_PopupMenu->setVisible(false);
            }
            break;
        case EVT_MouseRelease:
            if (m_ClickedCtl && m_HoveredCtl == m_ClickedCtl)
            {
                ctl = m_ClickedCtl;
                //default gui events
                switch (ctl->getType())
                {
                case GUI_Checkbox:
                    ctl->setValue(1 - ctl->getValue());
                    break;
                default:
                    break;
                }
                //gui click event
                if (ctl->getType() == GUI_Button ||
                    ctl->getType() == GUI_Checkbox)
                {
                    GLEvent evt;
                    evt.type = EVT_GuiClick;
                    evt.gui.ctl = ctl;
                    evt.gui.id = ctl->getId();
                    evt.gui.param = ctl->getValue();
                    onEvent(evt);
                }
            }
            m_ClickedCtl = NULL;
            break;
        case EVT_MouseWheel:
            if (m_HoveredCtl)
            {
                ctl = m_HoveredCtl;
                if (ctl->getParent() && ctl->getParent()->getType() == GUI_Listbox)
                    ctl = ctl->getParent();
                if (ctl->getType() == GUI_Listbox)
                {
                    int t = ctl->getTopItem() + e.mouse.wheel * 20;
                    ctl->setTopItem(t, true);
                }
            }
            break;
        default:
            break;
        }
    }
    //input state
    #if 1
    switch (e.type)
    {
    case EVT_KeyPress:
        keyboard.key[e.key.code] = true;
        break;
    case EVT_KeyRelease:
        keyboard.key[e.key.code] = false;
        break;
    case EVT_MouseMove:
        mouse.deltaX = e.mouse.x - mouse.x;
        mouse.deltaY = e.mouse.y - mouse.y;
        mouse.x = e.mouse.x;
        mouse.y = e.mouse.y;
        break;
    case EVT_MouseWheel:
        mouse.deltaWheel = e.mouse.wheel;
        mouse.wheel += e.mouse.wheel;
        break;
    case EVT_MousePress:
        mouse.button[e.mouse.button] = true;
        break;
    case EVT_MouseRelease:
        mouse.button[e.mouse.button] = false;
        break;
    default:
        break;
    }
    #endif
}


GLTask* GLClient::createTask(float timeout, int id, void* data)
{
    m_TaskList.push_back(GLTask(id, data, timeout));
    return &m_TaskList.back();
}

//
// Gui
//

void GLClient::initGui(int w, int h, GLFont* font)
{
    releaseGui();
    m_Desktop = new GLGuiControl(NULL, GUI_Desktop, 0, 0, w, h);
    m_Desktop->setFont(font);
    m_Desktop->setClient(this);
    m_PopupMenu = m_Desktop->createControl(GUI_Listbox, 0, 0, 100, 100);
    m_PopupMenu->setVisible(false);
}

bool GLClient::loadGuiXml(const char* file_name)
{
	TiXmlDocument doc(file_name);
	if (doc.LoadFile())
	{
        releaseGui();
        String fn(file_name);
	    m_DesktopXmlPath = fn.getSubString(0, fn.findLast('/'));
	    if (m_DesktopXmlPath == fn)
            m_DesktopXmlPath = fn.getSubString(0, fn.findLast('\\'));
	    m_DesktopXmlPath += '/';
		TiXmlElement* root = doc.FirstChildElement("desktop");
		parseGuiXmlNode(root, NULL);
		return true;
	}
	return false;
}

void GLClient::releaseGui()
{
    if (m_Desktop)
    {
        delete m_Desktop;
        m_Desktop = NULL;
        m_PopupMenu = NULL;
        m_HoveredCtl = NULL;
        m_ClickedCtl = NULL;
        m_ActiveCtl = NULL;
        m_DesktopXmlPath = "";
    }
}

GLGuiControl* GLClient::getGuiControl(int id)
{
    if (!m_Desktop)
        return NULL;
    return m_Desktop->getControlById(id);
}

GLGuiControl* GLClient::createControl(GLGuiType t, int x, int y, int w, int h, const char* text)
{
    GLGuiControl* ctl = m_Desktop->createControl(t, x, y, w, h);
    ctl->setText(text);
    //pushpopupmenu on top
    m_Desktop->swapControls(ctl, m_PopupMenu);
    return ctl;
}

void GLClient::showGuiMenu(int x, int y, GLGuiControl* ref_ctl, const char* menu_items, int id)
{
    if (m_PopupMenu && m_PopupMenu->getFont())
    {
        if (ref_ctl == NULL)
            ref_ctl = m_Desktop;
        x += ref_ctl->getBounds().AA.x;
        y += ref_ctl->getBounds().AA.y;
        m_PopupMenu->fillItems(menu_items);
        int n = m_PopupMenu->getItemsCount();
        int w = 0;
        int h = n * GUI_LISTITEM_HEIGHT;
        for (int i = 0; i < n; i++)
        {
            vector2i v = m_PopupMenu->getFont()->getFontInfo()->getTextSize(m_PopupMenu->getItem(i));
            if (v.x > w) w = v.x;
        }
        m_PopupMenu->setPosition(x, y, w+6, h);
        m_PopupMenu->setTopItem(0, false);
        m_PopupMenu->setId(id);
        m_PopupMenu->setVisible(true);
    }
}

//
//resources
//

GLTexture* GLClient::getTexture(const char* file_name, int flags)
{
    return m_Engine->getTexture(file_name, flags);
}

GLModel* GLClient::getModel(const char* file_name, float scale, int flags)
{
    return m_Engine->getModel(file_name, scale, flags);
}

GLFont* GLClient::getFont(const char* file_name)
{
    return m_Engine->getFont(file_name);
}

void GLClient::releaseTexture(GLTexture* tex)
{
    m_Engine->releaseTexture(tex);
}

void GLClient::releaseModel(GLModel* mdl)
{
    m_Engine->releaseModel(mdl);
}

void GLClient::releaseFont(GLFont* fnt)
{
    m_Engine->releaseFont(fnt);
}

//
// private
//

void GLClient::parseGuiXmlNode(TiXmlElement* elem, GLGuiControl* parent)
{
	//find control type
	GLGuiType type = GLGuiControl::parseGuiType(elem->Value());
	GLFont* font = NULL;
	GLTexture* image = NULL;
	int x = 0;
	int y = 0;
	int w = 0;
	int h = 0;
	int value = 0;
	int value_max = 100;
	int id = 0;
	bool visible = true;
	bool enabled = true;
	String text;
	int text_align = -1;
	int img_x = 0;
	int img_y = 0;
	int R=0, G=0, B=0;
	GLGuiControl* ctl = NULL;
	//parse attributes
	TiXmlAttribute* attr = elem->FirstAttribute();
	while (attr)
	{
		String s = attr->Name();
		const char* sval = attr->Value();
		if (s == "font")
        {
            String ss(m_DesktopXmlPath);
            ss += sval;
			font = getFont(ss.c_str());
        }
        else if (s == "image")
        {
            String ss(m_DesktopXmlPath);
            ss += sval;
			image = getTexture(ss.c_str(), GLTEX_EDGE);
        }
        else if (s == "image_size")
            sscanf(sval, "%i %i", &img_x, &img_y);
		else if (s == "size")
			sscanf(sval, "%i %i", &w, &h);
		else if (s == "pos")
			sscanf(sval, "%i %i %i %i", &x, &y, &w, &h);
		else if (s == "id")
			id = atoi(sval);
		else if (s == "value")
			value = atoi(sval);
		else if (s == "value_max")
			value_max = atoi(sval);
		else if (s == "color")
			sscanf(sval, "%i %i %i", &R, &G, &B);
		else if (s == "visible")
			visible = atoi(sval);
		else if (s == "enabled")
			enabled = atoi(sval);
		else if (s == "text")
			text = sval;
        else if (s == "text_align")
            text_align = GLGuiControl::parseGuiAlign(sval);
		attr = attr->Next();
	}
	if (w <= 0 || h <= 0)
        return;
    if (img_x == 0) img_x = mMin(w, h);
    if (img_y == 0) img_y = mMin(w, h);
	//create control
	if (parent == NULL)
    {
        //create desktop (root) control
        initGui(w, h, font);
        ctl = m_Desktop;
    }
    else
    {
        //create child control
        ctl = parent->createControl(type, x, y, w, h);
        if (font) ctl->setFont(font);
        ctl->setText(text);
        ctl->setId(id);
        ctl->setVisible(visible);
        ctl->setEnabled(enabled);
        ctl->setValue(value);
        ctl->setMaxValue(value_max);
        if (text_align > -1) ctl->setTextAlign(text_align);
        ctl->setImage(image);
        ctl->setImageSize(img_x, img_y);
        //push popupmenu on top
        if (parent == m_Desktop)
        {
            parent->swapControls(ctl, m_PopupMenu);
        }
    }
    //items
    if (elem->GetText())
    {
        ctl->fillItems(elem->GetText());
    }
	//recursion
	TiXmlElement* child = elem->FirstChildElement();
	while (child)
	{
		parseGuiXmlNode(child, ctl);
		child = child->NextSiblingElement();
	}
}

